<?php

namespace app\apiadmin\model;

use MongoDB\BSON\ObjectId;
use MongoDB\Client;
use MongoDB\Driver\Query;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException as Exception;
use think\db\exception\ModelNotFoundException;

class mongoModel
{

    private $client;

    private $database;

    private $connection; // 连接

    protected $collection; // 集合名称

    protected $pk; // 主键

    protected $key = []; // 查询主键

    protected $query = []; // 查询条件

    protected $data = []; // 插入数据

    protected $set = []; // 更新数据

    protected $options = []; // 配置

    protected function __construct()
    {
        $host = env('mongodb.host');
        $port = env('mongodb.port');
        $url = env('mongodb.url');
        $database = env('mongodb.database');
        $this->client = new Client($url);
        $this->database = $this->client->selectDatabase($database);
        $this->connection = $this->database->selectCollection($this->collection);

        return $this->connection;
    }

    /**
     * 获取主键
     *
     * */
    public static function getPk()
    {
        $model = new static();
        return $model->pk;
    }

    /**
     * query 条件
     * @param array $query 查询条件
     * @param array $options 字段限制与排序
     * @return $this
     *
     * */
    public function query(array $query = [])
    {
        if (!empty($query)) {
            $this->query = $query;
        }
        return $this;
    }

    public function options(array $options = [])
    {
        if (!empty($options)) {
            $this->options = $options;
        }
        return $this;
    }

    /**
     * skip 查询起始key
     * @param int $skip 查询起始key
     * @return $this
     *
     * */
    public function skip(int $skip = 0)
    {
        if (!empty($skip)) {
            $this->options['skip'] = $skip;
        }
        return $this;
    }

    /**
     * limit 查询条数
     * @param int $limit 查询条数
     * @return $this
     *
     * */
    public function limit(int $limit = 0)
    {
        if (!empty($limit)) {
            $this->options['limit'] = $limit;
        }
        return $this;
    }

    /**
     * update 更新数据
     * @param array $update 更新数据
     * @return $this
     *
     * */
    public function set(array $update = [])
    {
        if (!empty($update)) {
            $this->set = $update;
        }
        return $this;
    }

    public function sort(array $sort = [])
    {
        if (!empty($sort)) {
            $this->options['sort'] = $sort;
        }
        return $this;
    }

    /**
     * 保存记录 自动判断insert或者update
     * @access public
     * @param array $data        数据
     * @param bool  $forceInsert 是否强制insert
     * @return array
     */
    public function save(array $data = [], bool $forceInsert = false)
    {
        $isUpdate = true;

        return $isUpdate ? $this->update() : $this->insert();
    }

    /**
     * 插入记录
     * @access public
     * @param array   $data         数据
     * @param boolean $getLastInsID 返回自增主键
     * @return array
     */
    public function insert(array $data = [])
    {
        if (!empty($data)) {
            $this->data = $data;
        }
        $result = $this->connection->insertOne($this->data);
        $set = [
            'insertedCount' => $result->getInsertedCount(),
            'insertedId' => $result->getInsertedId(),
        ];
        return $set;
    }

    /**
     * 更新记录
     * @access public
     * @param mixed $data 数据
     * @return array
     * @throws Exception
     */
    public function update(array $data = [])
    {
        if (empty($data['query']) && empty($this->query)) {
            throw new Exception('查询条件不得为空');
        }
        if (empty($data['set']) && empty($this->set)) {
            throw new Exception('更新数据不得为空');
        }
        if (empty($data['options']) && empty($this->options)) {
            $data['options'] = ['upsert' => false];
        }
        $this->query = array_merge($this->query ?? [], $data['query']);
        $this->set = array_merge($this->set ?? [], $data['set']);
        $this->options = array_merge($this->options ?? [], $data['options']);

        $result = $this->connection->updateOne($this->query, $this->set, $this->options);
        $set = [
            'matchedCount' => $result->getMatchedCount(), // 匹配到的记录数
            'modifiedCount' => $result->getModifiedCount(), // 更新记录数
            'upsertedCount' => $result->getUpsertedCount(), // 插入记录数
            'upsertedId' => $result->getUpsertedId(), // 插入记录id
        ];
        return $set;
    }

    /**
     * 删除记录.
     *
     * @param mixed $data 表达式 true 表示强制删除
     *
     * @throws Exception
     *
     * @return int
     */
    public function delete($id = null): int
    {
        if (empty($id) && empty($this->key)) {
            throw new Exception('删除ID不的为空');
        }
        if (!empty($id)) {
            $this->key = [$this->pk => new ObjectId($id)];
            $result = $this->connection->deleteOne($this->key);
        } elseif (!empty($this->query)) {
            $result = $this->connection->deleteMany($this->query);
        } else {
            throw new Exception('删除条件不的为空');
        }

        return $result->getDeletedCount();
    }

    /**
     * 查找记录.
     *
     * @param array $data 主键数据
     *
     * @throws Exception
     * @throws ModelNotFoundException
     * @throws DataNotFoundException
     *
     * @return array|static[]
     */
    public function select()
    {

        if (empty($this->query)) {
            $this->query = [];
        }
        if (empty($this->options)) {
            $this->options = [];
        }
        $cursor = $this->connection->find($this->query, $this->options);

        $results = [];
        foreach ($cursor as $document) {
            $results[] = $document;
        }

        return $results;
    }

    public function count()
    {
        if (empty($this->query)) {
            $this->query = [];
        }
        if (empty($this->options)) {
            $this->options = [];
        }
        //$q = [
        //    'user_id' => 2,
        //    'del' => 0
        //];
        //$query = new Query($q, []);
        //return $this->connection->countDocuments($query);
        return $this->connection->countDocuments($this->query, $this->options);
    }
    public function sum($fieldName)
    {
        $pipeline = [
            [
                '$group' => [
                    '_id' => null, // 不分组，对所有文档计算总和
                    'total' => [
                        '$sum' => '$' . $fieldName, // 替换为你要计算总和的字段名
                    ],
                ],
            ],
        ];

        $options = [
            'allowDiskUse' => true, // 如果结果集很大，允许使用临时文件
        ];

        $result = $this->connection->aggregate($pipeline, $options);

        $total = 0;
        if (!$result) {
            return $total;
        }
        $data = $result->toArray();
        if (count($data) <= 0) {
            return $total;
        }
        $total = $data[0]->getArrayCopy()['total'];
        return $total;
    }

    public function groupSumList($groupField, $sumField)
    {
        $pipeline = [
            [
                '$group' => [
                    '_id' => [
                        '$dateToString' => [
                            'format' => '%Y-%m-%d %H:%M:00',
                            'date' => "$" . $groupField,
                            'timezone' => "Asia/Shanghai",
                        ],

                    ], // 不分组，对所有文档计算总和
                    'total' => [
                        '$sum' => "$" . $sumField,
                    ],
                    'total_in' => [
                        '$sum' => "$" . "incount",
                    ],
                    'total_out' => [
                        '$sum' => "$" . "outcount",
                    ],
                    'count' => [
                        '$sum' => 1,
                    ],
                ],
            ],
            [
                '$sort' => ['_id' => 1],
            ],
            [
                '$project' => [
                    '_id' => 0,
                    'statistics_time' => '$_id',
                    'online_user_nums' => '$total',
                    'online_in' => '$total_in',
                    'online_out' => '$total_out',
                    'total_count' => '$count',
                ],
            ],
        ];
        $options = [
            'allowDiskUse' => true, // 如果结果集很大，允许使用临时文件
        ];

        $result = $this->connection->aggregate($pipeline, $options)->toArray();

        return $result;
    }
    /**
     * 查找单条记录.
     *
     * @param mixed $data 主键数据
     *
     * @throws Exception
     * @throws ModelNotFoundException
     * @throws DataNotFoundException
     *
     * @return mixed
     */
    public function findOne($data = null)
    {
        if (!is_null($data)) {
            $this->query = $data;
        }

        if (empty($this->query)) {
            $result = [];
        } else {
            $result = $this->connection->findOne($this->query, $this->options);
        }

        return $result;
    }

    /***************************** 静态方法 *********************************************/

    /**
     * 插入记录
     *
     * */
    public static function add($param)
    {
        $filesModel = new static();
        return $filesModel->save($param, true);
    }

    /**
     * 更新记录
     *
     * */
    public static function edit($id, $param)
    {
        $filesModel = new static();
        return $filesModel->query(['id' => $id])->save($param);
    }

    /**
     * 更新或插入记录
     *
     * */
    public static function addOrEdit($param)
    {
        if (empty($param)) {
            return false;
        }
        $model = new static();
        $_key = $model->pk;
        if (!empty($param['query'][$_key])) {
            $param['query'][$_key] = new ObjectId($param['query'][$_key]);
        }
        return $model->update($param);
    }

    /**
     * 查询记录.
     *
     * @param mixed $id  主键
     *
     * @return bool
     */
    public static function find(array $query = [])
    {
        if (!empty($query)) {
            $model = new static();
            $model->query($query);
            $result = $model->findOne();
        } else {
            $result = [];
        }
        return $result;
    }

}
